"""
@author: Sergiy Tsymbalyuk
"""

from datetime import datetime, date
from hashlib import md5

from pylons import tmpl_context as c
from pylons.i18n import lazy_ugettext as _

from babel import dates
from formalchemy import FieldSet, fatypes, fields, Field, validators, helpers as h
from formalchemy.templates import TemplateEngine

from webhelpers.html import literal
from webhelpers.html.tags import select, text
from webhelpers.html.builder import HTML
from webhelpers.containers import distribute
from chowa.lib.base import render


class ChowaDateFieldRenderer(fields.FieldRenderer):
    def render(self, **kwargs):
        _value = ''
        if self.value:
            _date = datetime.strptime(self.value, '%Y-%m-%d')
            # FIXME: formate date according to locale
            _value = datetime.strftime(_date, '%d-%m-%Y')
        
        return text(self.name, value=_value, readonly=True, class_='date')

    def _serialized_value(self):
        std_date = date.today().strftime('%F')
        i18n_date = self.params.getone(self.name)
        if i18n_date:
            _date = dates.parse_date(i18n_date)
            std_date = datetime.strftime(_date, '%Y-%m-%d')
        return std_date


def password_length(length=6):
    def validator(value):
        if hasattr(value, 'original') and len(value.original) < length:
            raise validators.ValidationError(_("The password too short"))
    return validator

def password_ascii(value):
    try:
        str(value.original)
    except:
        raise validators.ValidationError(_("The password isn't ASCII"))

def password_alphanum(value):
    if value.original.isdigit():
        raise validators.ValidationError(_("The password contain only digits"))

class ChowaPasswordFieldRenderer(fields.PasswordFieldRenderer):
    def render(self, **kwargs):
        return HTML.tag('input',
                        type='password',
                        id=self.name,
                        name=self.name)
    def _serialized_value(self):
        class password(str): pass
        passwd = self.params.getone(self.name)
        if passwd:
            try:
                hashed_passwd = md5(passwd).hexdigest()
            except:
                value = password(-1)
                value.original = passwd
                return value
            value = password(hashed_passwd)
            value.original = passwd
        else:
            value = self.field.model_value
        return value

# FIXME: copy-paste from formalchemy.fields
def _extract_options(options):
    if isinstance(options, dict):
        options = options.items()
    for choice in options:
        # Choice is a list/tuple...
        if isinstance(choice, (list, tuple)):
            if len(choice) != 2:
                raise Exception('Options should consist of two items, a name and a value; found %d items in %r' % (len(choice, choice)))
            yield choice
        # ... or just a string.
        else:
            if not isinstance(choice, basestring):
                raise Exception('List, tuple, or string value expected as option (got %r)' % choice)
            yield (choice, choice)
 

class ChowaCheckBoxSet(fields.CheckBoxSet):
    def render(self, options, **kwargs):
        value = self.value
        self.radios = []
        if callable(options):
            options = options(self.field.parent)
        for i, (choice_name, choice_value) in enumerate(_extract_options(options)):
            choice_id = '%s_%i' % (self.name, i)
            radio = self.widget(self.name, choice_value, id=choice_id,
                                checked=self._is_checked(choice_value, value),
                                style='width:20px;', **kwargs)
            label = HTML.tag('label', c=choice_name, for_=choice_id)
            self.radios.append(HTML.literal(radio + label))
        
        
        table = distribute(self.radios, 2, 'V')
        trs = HTML.literal()
        for row in table:
            tds = HTML.literal()
            for item in row:
                if not item is None:
                    tds += HTML.tag('td', item)
            trs += HTML.tag('tr', tds)
        return HTML.tag('fieldset', HTML.tag('table', c=trs, style='width:100%;'), style='margin:0px;')
    

class TabularEngine(TemplateEngine):
    filters = {}
    
    def render(self, template_name, **kwargs):
        fieldset = kwargs['fieldset']
        # hide field if no label
        c.tr_style = lambda field: 'display:none' if isinstance(field.renderer, fields.HiddenFieldRenderer) else ''
        
        # if field has verbose name use it instead of field name
        for field in fieldset.render_fields.itervalues():
            field.filter = self.filters.get(field.name)
            try:
                if field._columns[0].info.has_key('verbose'):
                    field.set(label=field._columns[0].info['verbose'])
            except:
                pass
        kwargs['types'] = fatypes
        return render('/forms/tabular_fieldset.html', extra_vars=kwargs)


class TabularFieldSet(FieldSet):
    _null_option = ('--', '')
    default_renderers = {
        fatypes.String: fields.TextFieldRenderer,
        fatypes.Unicode: fields.TextFieldRenderer,
        fatypes.Text: fields.TextFieldRenderer,
        fatypes.Integer: fields.IntegerFieldRenderer,
        fatypes.Float: fields.FloatFieldRenderer,
        fatypes.Numeric: fields.FloatFieldRenderer,
        fatypes.Interval: fields.IntervalFieldRenderer,
        fatypes.Boolean: fields.SelectFieldRenderer,
        fatypes.DateTime: fields.DateTimeFieldRenderer,
        fatypes.Date: ChowaDateFieldRenderer,
        fatypes.Time: fields.TimeFieldRenderer,
        fatypes.LargeBinary: fields.FileFieldRenderer,
        fatypes.List: fields.SelectFieldRenderer,
        fatypes.Set: fields.SelectFieldRenderer,
        'dropdown': fields.SelectFieldRenderer,
        'checkbox': ChowaCheckBoxSet,
        'radio': fields.RadioSet,
        'password': ChowaPasswordFieldRenderer,
        'textarea': fields.TextAreaFieldRenderer,
    }
    engine = TabularEngine()
    
    def __init__(self, *args, **kwargs):
        FieldSet.__init__(self, *args, **kwargs)
        options = []
        if '_password' in self._fields:
            # validators
            def same_password(value, field=None):
                if field.parent._password.value != value:
                    raise validators.ValidationError(_("The password do not match"))
            
            repeat_password = Field('repeat_password').password() \
                .validate(same_password) \
                .label(_('Repeat password'))
            self.insert_after(self._password, repeat_password)
            options += [self._password.password()]
        for field in self._fields:
            if self[field].is_collection:
                options += [self[field].checkbox()]
        self.configure(options=options)
    